﻿using System;
using System.Windows;
using Microsoft.Deployment.WindowsInstaller;
using wpfsetup.Setup.Model;
using wpfsetup.Setup.Resources;

namespace wpfsetup.Setup.ViewModel
{
    public class ExternalUIHandlerViewModel : BaseViewModel
    {
        #region Fields

        private IInstallerAdapter _installerAdapter;
        private bool _firstTime;
        private bool _enableActionData;
        private int _progressTotal;
        private string _actionDataText;
        private int _progress;
        private bool _inProgress;
        private FlowDirection _progressDirection;
        private int _actionDataProgress;
        private bool _cancelInstall;
        private int _langId;
        private string _caption;
        private bool _enableCancelInstall;

        #endregion // Fields

        #region Constructor

        public ExternalUIHandlerViewModel(IInstallerAdapter installerAdapter)
        {
            this._installerAdapter = installerAdapter;
            this._installerAdapter.SetInternalUI(InstallUIOptions.Silent|InstallUIOptions.SourceResolutionOnly);
            this._installerAdapter.SetExternalUI(this.BasicUIHandler, InstallLogModes.ActionData |
                                                                      InstallLogModes.ActionStart |
                                                                      InstallLogModes.CommonData | 
                                                                      InstallLogModes.Error |
                                                                      InstallLogModes.FatalExit |
                                                                      InstallLogModes.Info |
                                                                      InstallLogModes.Initialize |
                                                                      InstallLogModes.OutOfDiskSpace |
                                                                      InstallLogModes.Progress |
                                                                      InstallLogModes.ResolveSource |
                                                                      InstallLogModes.ShowDialog |
                                                                      InstallLogModes.Terminate | 
                                                                      InstallLogModes.User |
                                                                      InstallLogModes.Warning);
            this._firstTime = true;
            this._cancelInstall = false;
        }

        

        #endregion // Constructor

        #region Properties

        public bool EnableActionData
        {
            get
            {
                return _enableActionData;
            }
            set
            {
                if (value == _enableActionData)
                    return;

                _enableActionData = value;
                this.OnPropertyChanged("EnableActionData");
            }
        }

        public int ProgressTotal
        {
            get {
                return _progressTotal;
            }
            set {
                if(value == _progressTotal)
                    return;

                _progressTotal = value;
                this.OnPropertyChanged("ProgressTotal");
            }
        }

        public string ActionDataText
        {
            get {
                return _actionDataText;
            }
            set {
                if (value == _actionDataText)
                    return;
                
                _actionDataText = value;
                this.OnPropertyChanged("ActionDataText");
            }
        }

        public int Progress
        {
            get {
                return _progress;
            }
            set {
                if (value == _progress)
                    return;
                
                _progress = value;
                this.OnPropertyChanged("Progress");
            }
        }

        public bool InProgress
        {
            get {
                return _inProgress;
            }
            set {
                if (value == _inProgress)
                    return;

                _inProgress = value;
                this.OnPropertyChanged("InProgress");
            }
        }

        public FlowDirection ProgressDirection
        {
            get {
                return _progressDirection;
            }
            set {
                if (value == _progressDirection)
                    return;

                _progressDirection = value;
                this.OnPropertyChanged("ProgressDirection");
            }
        }

        public int ActionDataProgress
        {
            get {
                return _actionDataProgress;
            }
            set {
                if (value == _actionDataProgress)
                    return;

                _actionDataProgress = value;
                this.OnPropertyChanged("ActionDataProgress");
            }
        }

        public bool CancelInstall
        {
            get {
                return _cancelInstall;
            }
            set {
                if(value == _cancelInstall)
                    return;

                _cancelInstall = value;
                this.OnPropertyChanged("CancelInstall");
            }
        }

        public int LangID
        {
            get {
                return _langId;
            }
            set {
                if(value == _langId)
                    return;

                _langId = value;
                this.OnPropertyChanged("LangID");
            }
        }

        public string Caption
        {
            get {
                return _caption;
            }
            set {
                if (value == _caption)
                    return;

                _caption = value;
                this.OnPropertyChanged("Caption");
            }
        }

        public bool EnableCancelInstall
        {
            get {
                return _enableCancelInstall;
            }
            set {
                if (value == _enableCancelInstall)
                    return;

                _enableCancelInstall = value;
                this.OnPropertyChanged("EnableCancelInstall");
            }
        }

        #endregion // Properties

        #region Methods

        public MessageResult BasicUIHandler(InstallMessage messagetype, string message, MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultbutton)
        {
            if (this._firstTime)
            {
                this._installerAdapter.SetInternalUI(InstallUIOptions.Basic);
                this._firstTime = false;
            }

            if (message.Length == 0)
                return MessageResult.None;

            switch (messagetype)
            {
                case InstallMessage.FatalExit:
                    return User32.MessageBox(IntPtr.Zero, message, Strings.MessageBox_FatalError,
                                             User32.Options(buttons, icon, defaultbutton));
                    break;
                case InstallMessage.Error:
                    return User32.MessageBox(IntPtr.Zero, message, Strings.MessageBox_Error,
                                             User32.Options(buttons, icon, defaultbutton));
                    break;
                case InstallMessage.Warning:
                    return User32.MessageBox(IntPtr.Zero, message, Strings.MessageBox_Warning,
                                             User32.Options(buttons, icon, defaultbutton));
                    break;
                case InstallMessage.User:
                    return User32.MessageBox(IntPtr.Zero, message, Strings.MessageBox_User,
                                             User32.Options(buttons, icon, defaultbutton));
                    break;
                case InstallMessage.Info:
                    return MessageResult.OK;
                    break;
                case InstallMessage.FilesInUse:
                    return MessageResult.None;
                    break;
                case InstallMessage.ResolveSource:
                    // Always return 0 for ResolveSource
                    return MessageResult.None;
                    break;
                case InstallMessage.OutOfDiskSpace:
                    return MessageResult.OK;
                    break;
                case InstallMessage.ActionStart:
                    this.EnableActionData = false;
                    break;
                case InstallMessage.ActionData:
                    if (this.ProgressTotal > 0)
                        this.ActionDataText = message;
                    if (this.EnableActionData)
                    {
                        if (this.ProgressDirection == FlowDirection.LeftToRight)
                            this.Progress += this.ActionDataProgress;
                        else
                            this.Progress -= this.ActionDataProgress;
                    }
                    return MessageResult.OK;
                    break;
                case InstallMessage.Progress:
                    var progressMessage = ParseProgressMessage(message);
                    if (progressMessage.isValid)
                    {
                        if (progressMessage.field1 == 0)
                        {
                            this.ProgressTotal = progressMessage.field2;
                            if (progressMessage.field3 == 0)
                            {
                                this.ProgressDirection = FlowDirection.LeftToRight;
                                this.Progress = 0;
                            }
                            else
                            {
                                this.ProgressDirection = FlowDirection.RightToLeft;
                                this.Progress = this.ProgressTotal;
                            }
                            this.InProgress = progressMessage.field4 == 0;
                        }
                        if (progressMessage.field1 == 1)
                        {
                            if (progressMessage.field3 == 1)
                            {
                                this.ActionDataProgress = progressMessage.field2;
                                this.EnableActionData = true;
                            }
                            else
                            {
                                this.EnableActionData = false;
                            }

                        }
                        if (progressMessage.field1 == 2)
                        {
                            if (this.ProgressTotal != 0)
                            {
                                if (this.ProgressDirection == FlowDirection.LeftToRight)
                                    this.Progress += progressMessage.field2;
                                else
                                    this.Progress -= progressMessage.field2;
                            }
                        }
                        if (progressMessage.field1 == 3)
                        {
                            this.ProgressTotal += progressMessage.field2;
                        }
                    }
                    return this.CancelInstall ? MessageResult.Cancel: MessageResult.OK;
                    break;
                case InstallMessage.CommonData:
                    var commonDataMessage = ParseCommonDataMessage(message);
                    if (commonDataMessage.isValid)
                    {
                        if(commonDataMessage.field1 == 0)
                        {
                            this.LangID = int.Parse(commonDataMessage.field2);
                        }
                        if(commonDataMessage.field1 == 1)
                        {
                            this.Caption = commonDataMessage.field2;
                        }
                        if(commonDataMessage.field1 == 2)
                        {
                            this.EnableCancelInstall = int.Parse(commonDataMessage.field2) == 1;
                        }
                    }
                    return MessageResult.OK;
                    break;
                case InstallMessage.Initialize:
                    return MessageResult.OK;
                    break;
                case InstallMessage.Terminate:
                    return MessageResult.OK;
                    break;
                case InstallMessage.ShowDialog:
                    return MessageResult.OK;
                    break;
            }
            return MessageResult.None;
        }

        #endregion // Methods

        #region Private Helpers

        static ProgressMessage ParseProgressMessage(string message)
        {
            var split = message.Split(' ');
            var result = new ProgressMessage
                             {
                                 isValid = split.Length%2 == 0 && split.Length >= 4
                             };
            if (result.isValid)
            {
                result.field1 = int.Parse(split[1]);
                result.field2 = int.Parse(split[3]);
                if (split.Length > 4)
                    result.field3 = int.Parse(split[5]);
                if (split.Length > 6)
                    result.field4 = int.Parse(split[7]);
            }
            return result;
        }

        class ProgressMessage
        {
            internal int field1;
            internal int field2;
            internal int field3;
            internal int field4;
            internal bool isValid;
        }

        static CommonDataMessage ParseCommonDataMessage(string message)
        {
            var split = message.Split(' ');
            var result = new CommonDataMessage
                             {
                                 isValid = split.Length%2 == 0 && split.Length >= 4
                             };
            if (result.isValid)
            {
                result.field1 = int.Parse(split[1]);
                result.field2 = split[3]; 
            }
            return result;
        }

        class CommonDataMessage
        {
            internal int field1;
            internal string field2;
            internal bool isValid;
        }

        #endregion // Private Helpers
    }
}
